import json
import os
from langchain_community.llms.tongyi import Tongyi
from langchain.prompts import ChatPromptTemplate,MessagesPlaceholder,PromptTemplate
from dotenv import load_dotenv
from langchain.tools.retriever import create_retriever_tool
# from .plan_RAG import exercise     # 移除RAG功能，直接使用大模型
from langchain.tools import tool
from langchain.agents import create_openai_functions_agent, AgentExecutor
from langchain_openai import ChatOpenAI
from report.models import UserWorkoutHistory, Exercise, WorkoutPlan
from typing import List, Dict, Any
from .report_builder import ConcreteWorkoutPlanBuilder



load_dotenv()


llm = ChatOpenAI(
    model="qwen-max",
    api_key=os.getenv('DASHSCOPE_API_KEY'),
    base_url='https://dashscope.aliyuncs.com/compatible-mode/v1',
    # 移除max_tokens限制，让模型自己决定输出长度
    temperature=0.7,  # 增加一些创造性
)



# agent构建流程  1.工具
# 直接使用大模型搜索健身动作
@tool
def search_exercises(query: str) -> str:
    """根据用户需求搜索和推荐相关的健身动作。"""
    prompt_template = PromptTemplate(
        input_variables=['query'],
        template="""
        作为专业健身教练，请根据用户的需求"{query}"推荐合适的健身动作。
        
        请考虑以下因素：
        1. 动作的适用性（初学者/中级/高级）
        2. 目标肌群
        3. 动作难度
        4. 安全性
        5. 训练效果
        
        请返回3-5个最合适的动作，格式如下：
        {{
            "recommended_exercises": [
                {{
                    "name": "动作名称",
                    "target_muscles": "目标肌群",
                    "difficulty": "难度级别",
                    "description": "动作描述",
                    "benefits": "训练效果",
                    "tips": "注意事项"
                }}
            ]
        }}
        """
    )

    exercise_chain = prompt_template | llm
    # 添加类型注解
    response = exercise_chain.invoke({'query': query})
    return response

# 第二个工具:获取用户的训练历史记录
@tool
def analyze_user_history(user_id: int) -> str:
    """分析用户的训练历史数据，提供训练习惯和表现分析。"""
    try:   # 获取用户训练历史记录
        history = UserWorkoutHistory.objects.filter(user_id=user_id) \
                      .select_related('exercise') \
                      .order_by('-completed_at')[:50]

        if not history:
            return "该用户暂无训练历史数据"

        # 计算统计数据
        total_sessions = len(history)
        avg_score = sum([h.performance_score for h in history]) / total_sessions

        # 获取最常做的动作
        from collections import Counter
        exercise_counter = Counter()
        for record in history:
            exercise_counter[record.exercise.name] += 1
        favorite_exercises = [ex[0] for ex in exercise_counter.most_common(3)]

        analysis = f"""
        用户训练分析报告:
        - 总训练次数: {total_sessions}
        - 平均表现评分: {avg_score:.2f}/1.0
        - 最常做的动作: {', '.join(favorite_exercises)}
        - 最近训练: {history[0].exercise.name} (评分: {history[0].performance_score})
        """

        return analysis
    except Exception as e:
        return f"分析用户历史失败: {str(e)}"


@tool
def recommend_exercises_based_on_goals(goals: str, fitness_level: str = "intermediate") -> str:
    """根据用户目标和健身水平推荐合适的健身动作。"""
    prompt_template = PromptTemplate(
        input_variables=["goals", "fitness_level"],
        template="""
        作为专业健身教练，请根据以下信息推荐健身动作:

        用户目标: {goals}
        健身水平: {fitness_level}

        请推荐3-5个最适合的动作，并说明推荐理由。
        返回简洁的JSON格式:
        {{
            "recommended_exercises": [
                {{
                    "name": "动作名称",
                    "reason": "推荐理由",
                    "difficulty": "难度级别",
                    "target_muscles": "目标肌群",
                    "sets_reps": "建议组数和次数",
                    "rest_time": "休息时间"
                }}
            ]
        }}
        """
    )
    
    chain = prompt_template | llm
    response = chain.invoke({"goals": goals, "fitness_level": fitness_level})
    return response


@tool
def search_fitness_knowledge(query: str) -> str:
    """搜索健身相关的知识、技巧和最新研究。"""
    prompt_template = PromptTemplate(
        input_variables=['query'],
        template="""
        作为专业健身教练和营养师，请针对用户的问题"{query}"提供详细的健身知识和建议。
        
        请包含以下内容：
        1. 科学原理和理论基础
        2. 具体的训练方法和技术要点
        3. 注意事项和安全提醒
        4. 营养建议（如果相关）
        5. 常见错误和避免方法
        6. 进阶建议
        
        请用专业但易懂的语言回答，确保信息准确、实用。
        """
    )
    
    knowledge_chain = prompt_template | llm
    res = knowledge_chain.invoke({'query': query})
    return res


@tool
def generate_workout_plan(exercises: List[str], duration: int = 30, difficulty: str = "intermediate") -> str:
    """根据提供的动作列表生成完整的健身计划。"""
    prompt_template = PromptTemplate(
        input_variables=["exercises", "duration", "difficulty"],
        template="""
        请根据以下动作生成一个{duration}分钟的{difficulty}难度健身计划:

        可用动作: {exercises}

        请安排:
        1. 热身运动 (5分钟)
        2. 主要训练动作及组数次数
        3. 休息时间安排
        4. 拉伸放松 (5分钟)

        请生成简洁但完整的JSON格式响应，确保包含所有必要信息：
        {{
            "title": "训练计划标题",
            "description": "简要描述",
            "warmup": "热身建议",
            "main_workout": [
                {{
                    "exercise": "动作名称",
                    "sets": 组数,
                    "reps": "次数范围",
                    "rest": "休息时间",
                    "notes": "注意事项"
                }}
            ],
            "cooldown": "拉伸建议",
            "total_duration": {duration},
            "difficulty": "{difficulty}",
            "tips": "训练建议"
        }}
        """
    )
    
    chain = prompt_template | llm
    response = chain.invoke({
        "exercises": ", ".join(exercises),
        "duration": duration,
        "difficulty": difficulty
    })
    return response



# 2.工具列表
tools = [search_exercises, analyze_user_history, recommend_exercises_based_on_goals, search_fitness_knowledge, generate_workout_plan]




# 这里使用Chat模版
prompt = ChatPromptTemplate.from_messages([
    {'role':'system','content':'你是一个专业健身教练,可根据用户需求提供个性化的健身建议和计划。请确保在回答中包含具体的健身动作名称。'},
    {'role':'user','content':"""
    请根据以下信息为用户制定个性化训练计划：
    
    训练目标: {goals}
    健身水平: {fitness_level}
    训练时长: {duration}分钟
    个人偏好: {preferences}
    
    如果提供了用户ID，请先使用analyze_user_history工具分析用户的训练历史，然后基于历史数据制定更个性化的计划。
    请充分利用用户的训练历史数据，包括：
    - 用户最常做的动作
    - 平均表现评分
    - 训练习惯和偏好
    - 最近训练记录
    
    请提供详细的训练计划，包括：
    1. 热身运动（5分钟）
    2. 主要训练动作（至少3个动作）
    3. 每个动作的组数和次数
    4. 休息时间安排
    5. 拉伸放松（5分钟）
    
    请确保在回答中包含具体的健身动作名称，如：深蹲、俯卧撑、平板支撑、仰卧起坐、引体向上、硬拉、卧推、划船、推举、弯举、臂屈伸、卷腹、桥式、弓步蹲等。
    
    重要：请使用以下标准格式输出计划描述，确保美观和易读：
    
    【热身运动】(5分钟)
    • 轻松跑步或快走：2分钟
    • 动态拉伸(如腿部摆动、手臂圈)：3分钟
    
    【主要训练动作】
    • 动作名称1：
      - 组数：3组
      - 次数：每组10-15次
      - 休息：每组后休息45秒
      - 说明：动作的具体作用和注意事项
    
    • 动作名称2：
      - 组数：3组
      - 次数：每组30秒
      - 休息：每组后休息30秒
      - 说明：动作的具体作用和注意事项
    
    【拉伸放松】(5分钟)
    • 静态拉伸：针对主要肌群，每个动作保持20-30秒
    • 呼吸：缓慢而深长地呼吸
    
    【注意事项】
    • 根据个人情况适当调整组数和次数
    • 保持正确的动作姿势
    • 如有不适请立即停止
    """},
    MessagesPlaceholder(variable_name="agent_scratchpad")
])


# 3.构建agent实例
agent = create_openai_functions_agent(
    llm=llm,
    tools=tools,
    prompt=prompt
)

# 4.agent执行器
executor = AgentExecutor(
    agent=agent,
    tools=tools,
    verbose=True,
    handle_parsing_errors=True
)



# 5.调用-->在接口中调用
# res = executor.invoke({})


class PlanParser:
    """计划解析器 - 用于解析AI生成的计划并转换为数据库对象"""

    @staticmethod
    def parse_ai_plan(ai_response: str) -> Dict:
        """解析AI返回的计划内容"""
        try:
            # 尝试从响应中提取JSON
            if '```json' in ai_response:
                json_str = ai_response.split('```json')[1].split('```')[0].strip()
            elif '```' in ai_response:
                # 处理没有json标记的情况
                json_str = ai_response.split('```')[1].strip()
            else:
                json_str = ai_response

            plan_data = json.loads(json_str)
            print(f"✅ 成功解析JSON计划数据")
            return plan_data
        except json.JSONDecodeError as e:
            print(f"⚠️ JSON解析失败: {e}")
            print(f"原始响应: {ai_response[:200]}...")
            # 如果JSON解析失败，尝试提取关键信息
            return PlanParser._extract_plan_from_text(ai_response)

    @staticmethod
    def _extract_plan_from_text(text: str) -> Dict:
        """从文本中提取计划信息"""
        print("🔍 从文本中提取计划信息...")
        
        # 从AI响应中提取动作信息
        exercises = []
        
        # 常见的动作关键词和对应的数据库名称
        exercise_mapping = {
            "深蹲": "标准深蹲",
            "俯卧撑": "标准俯卧撑", 
            "平板支撑": "平板支撑",
            "仰卧起坐": "仰卧起坐",
            "引体向上": "引体向上",
            "硬拉": "硬拉",
            "卧推": "卧推",
            "划船": "划船",
            "推举": "推举",
            "弯举": "哑铃弯举",
            "臂屈伸": "臂屈伸",
            "卷腹": "卷腹",
            "桥式": "桥式",
            "弓步蹲": "弓步蹲",
            "跳跃": "跳跃",
            "跑步": "跑步",
            "游泳": "游泳",
            "骑行": "骑行",
            "瑜伽": "瑜伽",
            "拉伸": "拉伸",
            "跳绳": "跳绳",
            "波比跳": "波比跳",
            "开合跳": "开合跳"
        }
        
        # 从文本中提取动作
        for keyword, db_name in exercise_mapping.items():
            if keyword in text:
                # 尝试从文本中提取组数和次数
                sets = 3
                reps = "10-15"
                
                # 查找组数信息
                import re
                sets_match = re.search(r'(\d+)\s*组', text)
                if sets_match:
                    sets = int(sets_match.group(1))
                
                # 查找次数信息
                reps_match = re.search(r'(\d+)[-－](\d+)\s*次', text)
                if reps_match:
                    reps = f"{reps_match.group(1)}-{reps_match.group(2)}"
                
                exercises.append({
                    "exercise": db_name,
                    "sets": sets,
                    "reps": reps,
                    "rest": "60秒"
                })
                print(f"✅ 提取到动作: {db_name}")
        
        # 如果没找到动作，添加一些默认动作
        if not exercises:
            print("⚠️ 未找到动作，使用默认动作")
            exercises = [
                {
                    "exercise": "标准深蹲",
                    "sets": 3,
                    "reps": "10-15",
                    "rest": "60秒"
                },
                {
                    "exercise": "标准俯卧撑",
                    "sets": 3,
                    "reps": "8-12",
                    "rest": "60秒"
                }
            ]
        
        # 提取标题
        title = "AI生成健身计划"
        if "训练计划" in text:
            title_match = re.search(r'([^，。\n]+训练计划)', text)
            if title_match:
                title = title_match.group(1)
        
        # 提取难度
        difficulty = "intermediate"
        if "初学者" in text or "初级" in text:
            difficulty = "beginner"
        elif "高级" in text or "专业" in text:
            difficulty = "advanced"
        
        # 提取时长
        total_duration = 1800  # 默认30分钟
        duration_match = re.search(r'(\d+)\s*分钟', text)
        if duration_match:
            total_duration = int(duration_match.group(1)) * 60
        
        return {
            "title": title,
            "description": text[:500] + "..." if len(text) > 500 else text,
            "exercises": exercises,
            "difficulty": difficulty,
            "total_duration": total_duration,
            "rest_time": 30
        }


class DatabaseWriter:
    """数据库写入器 - 负责将计划写入数据库"""
    @staticmethod
    def save_workout_plan_to_db(user, plan_data: Dict) -> WorkoutPlan:
        """
        将AI生成的计划保存到数据库

        Args:
            user: 用户对象
            plan_data: 包含计划信息的字典

        Returns:
            WorkoutPlan: 保存后的计划对象

        Raises:
            ValueError: 当计划数据无效时
            Exception: 当数据库操作失败时
        """
        try:
            # 参数验证
            if not user or not plan_data:
                raise ValueError("用户和计划数据不能为空")

            # 使用建造者模式创建计划==>创建建造者实例对象
            builder = ConcreteWorkoutPlanBuilder(user)  # 具体建造者的实例对象

            # 设置基本信息：调用建造者方法
            builder.set_title(plan_data.get('title', 'AI生成计划'))
            builder.set_description(plan_data.get('description', ''))
            builder.set_difficulty(plan_data.get('difficulty', 'intermediate'))
            builder.set_total_duration(plan_data.get('total_duration', 1800))
            builder.set_rest_time(plan_data.get('rest_time', 30))

            # 添加动作
            exercises = plan_data.get('exercises', [])
            valid_exercise_count = 0
            
            # 获取数据库中所有动作，用于模糊匹配
            all_exercises = Exercise.objects.all()
            exercise_name_mapping = {}
            for ex in all_exercises:
                exercise_name_mapping[ex.name.lower()] = ex
                # 添加一些常见的别名
                if '深蹲' in ex.name:
                    exercise_name_mapping['深蹲'] = ex
                if '俯卧撑' in ex.name:
                    exercise_name_mapping['俯卧撑'] = ex
                if '平板支撑' in ex.name:
                    exercise_name_mapping['平板支撑'] = ex
                if '弯举' in ex.name:
                    exercise_name_mapping['弯举'] = ex
                if '跑步' in ex.name:
                    exercise_name_mapping['跑步'] = ex
                if '拉伸' in ex.name:
                    exercise_name_mapping['拉伸'] = ex
            
            for i, exercise_data in enumerate(exercises):
                exercise_name = exercise_data.get('exercise', '').strip()
                if exercise_name:
                    # 尝试精确匹配
                    exercise_obj = None
                    if exercise_name in exercise_name_mapping:
                        exercise_obj = exercise_name_mapping[exercise_name]
                    elif exercise_name.lower() in exercise_name_mapping:
                        exercise_obj = exercise_name_mapping[exercise_name.lower()]
                    else:
                        # 尝试模糊匹配
                        for db_name, db_exercise in exercise_name_mapping.items():
                            if exercise_name in db_name or db_name in exercise_name:
                                exercise_obj = db_exercise
                                break
                    
                    if exercise_obj:
                        # 解析组数和次数
                        sets = exercise_data.get('sets', 3)
                        reps = exercise_data.get('reps', 12)
                        
                        # 如果reps是字符串，尝试提取数字
                        if isinstance(reps, str):
                            import re
                            reps_match = re.search(r'(\d+)', reps)
                            if reps_match:
                                reps = int(reps_match.group(1))
                            else:
                                reps = 12
                        
                        builder.add_exercise(
                            exercise=exercise_obj,
                            sets=sets,
                            reps=reps,
                            order=i
                        )
                        valid_exercise_count += 1
                        print(f"✅ 成功添加动作: {exercise_obj.name}")
                    else:
                        print(f"⚠️ 无法匹配动作: {exercise_name}")

            # 检查是否至少有一个有效动作
            if valid_exercise_count == 0:
                # 如果没有有效动作，添加一些默认动作
                print("警告: 没有找到有效动作，添加默认动作")
                try:
                    # 尝试获取一些基本动作
                    default_exercises = Exercise.objects.all()[:3]
                    for i, exercise_obj in enumerate(default_exercises):
                        builder.add_exercise(
                            exercise=exercise_obj,
                            sets=3,
                            reps=12,
                            order=i
                        )
                        valid_exercise_count += 1
                except Exception as e:
                    print(f"添加默认动作失败: {e}")
                    raise ValueError("计划必须包含至少一个有效动作")

            # 构建并保存计划-->实例对象调用类方法！！不使用单例模式
            workout_plan = builder.build()   # 调用build池底存入数据库
            print(f"成功为用户 {user.username} 创建计划: {workout_plan.title}")
            return workout_plan  # 返回结果

        except ValueError as e:
            # 处理业务逻辑错误
            print(f"计划数据验证失败: {e}")
            raise

        except Exception as e:
            print(f"保存计划到数据库失败: {e}")
            raise